#include "stdafx.h"

#include <MyFC/Container.h>
#include <MyFC/Utility.h>
#include <algorithm>
#include <xtd/functional>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

Container::Container()
{
	m_pHoverControl = 0;
}

Container::~Container()
{
	ControlList::iterator it;
	for (it = m_Controls.begin(); it != m_Controls.end(); ++it) 
	{
		Control * pControl = * it;
		pControl->m_pParent = 0;
		pControl->m_Index = 0;
	}
}

Control * Container::GetHoverControl(CPoint p)
{
	ControlList::reverse_iterator it;
	for (it = m_Controls.rbegin(); it != m_Controls.rend(); ++it)
	{
		Control * pControl = * it;
		if (pControl->Visible() && pControl->Bounds().PtInRect(p))
			return pControl;
	}
	return 0;
}

void Container::Add(Control * pControl)
{
	if (pControl->m_pParent == this)
		return;
	assert(pControl->m_pParent == 0);

	bool controlFont = (pControl->Font() == 0);
	bool selfFont = (Font() != 0);

	bool updateFont = controlFont && selfFont;

	pControl->m_pParent = this;
	pControl->m_Index = m_Controls.insert(m_Controls.end(), pControl);
	if (updateFont)
		pControl->ProcessEvent(FontEvent());
}

bool Contains(Control * pParent, Control * pChild)
{
	for (Control * ptr = pChild; ptr != 0; ptr = ptr->Parent())
		if (ptr == pParent)
			return true;
	return false;
}

void Container::Remove(Control * pControl)
{
	assert(pControl->m_pParent == this);
	if (Contains(pControl, GetCaptureControl()))
		SetCaptureControl(0);
	if (Contains(pControl, GetFocusControl()))
		SetFocusControl(0);
	if (Contains(pControl, m_pHoverControl))
		m_pHoverControl = 0;
	m_Controls.erase(pControl->m_Index);
	pControl->m_pParent = 0;
	pControl->m_Index = 0;
//	if (pControl->Font() == 0 && Font() != 0)
//		pControl->ProcessEvent(FontEvent());
}

void Container::DispatchEvent(Event & e)
{
	std::for_each(m_Controls.begin(), m_Controls.end(),
		My::DispatchEvent(e));
}

void Container::ToFront(Control * pControl)
{
	assert(pControl->m_pParent == this);
	m_Controls.erase(pControl->m_Index);
	pControl->m_Index = m_Controls.insert(m_Controls.end(), pControl);
}

void Container::PaintChild(CDC * pDC, const CRect & rect, Control * pChild)
{
	if (pChild->GetWindow() != this->GetWindow())
		return;
	if (!pChild->Visible())
		return;
	CRect bounds = pChild->Bounds(), r(rect);
	if (!r.IntersectRect(&bounds, &rect) 
		&& !pChild->PaintOutside())
		return;
	CPoint p = pDC->SetWindowOrg(- pChild->GetWindowOffset());
	r.OffsetRect(- bounds.TopLeft());
	pChild->Paint(pDC, r);
	pDC->SetWindowOrg(p);
}

void Container::Paint(CDC * pDC, const CRect & r)
{
	DispatchEvent(WindowBoundsEvent(true));
	Control::Paint(pDC, r);
	ClipRect clip(this, pDC, ClientRect(), !PaintOutside());
	ControlList::iterator i;
	for (i = m_Controls.begin(); i != m_Controls.end(); ++i)
		PaintChild(pDC, r, * i);
}

Control * Container::GetCaptureControl() const
{
	return Parent() != 0 ? Parent()->GetCaptureControl() : 0;
}

Control * Container::GetFocusControl() const
{
	return Parent() != 0 ? Parent()->GetFocusControl() : 0;
}

void Container::SetCaptureControl(Control * pControl)
{
	if (Parent() != 0)
		Parent()->SetCaptureControl(pControl);
}

void Container::SetFocusControl(Control * pControl)
{
	if (Parent() != 0)
		Parent()->SetFocusControl(pControl);
}

void Container::ProcessEvent(Event & event)
{
	if (event.Is<MouseEnterEvent>()) 
	{
		MouseEnterEvent & e = event.As<MouseEnterEvent>();

//		assert(m_pHoverControl == 0);
		m_pHoverControl = GetHoverControl(e.m_Point);
		if (m_pHoverControl != 0) {
			e.m_Point -= m_pHoverControl->Offset();
			m_pHoverControl->ProcessEvent(event);
		} else
			Control::ProcessEvent(event);
		return;
	}
	if (event.Is<MouseLeaveEvent>()) 
	{
		if (m_pHoverControl != 0) {
			m_pHoverControl->ProcessEvent(event);
			m_pHoverControl = 0;
		} else
			Control::ProcessEvent(event);
		return;
	}
	if (event.Is<CursorEvent>())
	{
		if (GetCaptureControl() != this) 
		{
			CursorEvent & e = event.As<CursorEvent>();
			if (m_pHoverControl != 0) {
				e.m_Point -= m_pHoverControl->Offset();
				m_pHoverControl->ProcessEvent(event);
				if (e.m_Handled)
					return;
			}
		}
		Control::ProcessEvent(event);
		return;
	}
/*	if (event.Is<CommandEventBase>())
	{
		CommandEventBase & e = event.As<CommandEventBase>();
		ControlList::iterator i;
		for (i = m_Controls.begin(); i != m_Controls.end(); ++i) {
			(*i)->ProcessEvent(e);
			if (e.m_Handled)
				return;
		}
		Control::ProcessEvent(event);
		return;
	}
*/	if (event.Is<MouseEvent>())
	{
		if (GetCaptureControl() != this) 
		{
			MouseEvent & e = event.As<MouseEvent>();
			Control * pCtrl = GetHoverControl(e.m_Point);
			if (pCtrl != m_pHoverControl)
			{
				if (m_pHoverControl != 0)
					m_pHoverControl->ProcessEvent(MouseLeaveEvent());
				else
					Control::ProcessEvent(MouseLeaveEvent());
				m_pHoverControl = pCtrl;
				if (m_pHoverControl != 0)
					m_pHoverControl->ProcessEvent(MouseEnterEvent(e.m_Flags, e.m_Point - m_pHoverControl->Offset()));
				else
					Control::ProcessEvent(MouseEnterEvent(e.m_Flags, e.m_Point));
			}
			if (m_pHoverControl != 0) {
				e.m_Point -= m_pHoverControl->Offset();
				m_pHoverControl->ProcessEvent(event);
				return;
			}
		}
		Control::ProcessEvent(event);
		return;
	}

	if (event.Is<KeyEvent>())
	{
		Control::ProcessEvent(event);
		return;
	}

	if (event.Is<WindowEvent>())
	{
		DispatchEvent(event);
		return;
	}

	if (event.Is<BroadcastEvent>())
	{
		Control::ProcessEvent(event);
		DispatchEvent(event);
		return;
	}

	Control::ProcessEvent(event);
}

void Container::UpdateBounds()
{
	Control::UpdateBounds();
	LayoutControls();
	DispatchEvent(WindowBoundsEvent(false));
}

void Container::UpdateVisible()
{
	Control::UpdateVisible();
	DispatchEvent(WindowVisibleEvent(Visible()));
}

} // My

